package webx

import (
	"context"
	"fmt"
	"log/slog"
	"net/http"
	"os"
	"reflect"
	"strings"
	"sync"
	"time"

	"gitee.com/xungen/goweb/errno"
	"gitee.com/xungen/goweb/utils"
)

var initmutex sync.Mutex
var callbacks []func() error

func Run() {
	args := os.Args
	argc := len(args)
	if argc > 1 {
		LoadConfig(args[1])
	} else if path := "conf/config.yml"; utils.IsFile(path) {
		LoadConfig(path)
	} else if path := "etc/config.yml"; utils.IsFile(path) {
		LoadConfig(path)
	} else if path := "config.yml"; utils.IsFile(path) {
		LoadConfig(path)
	}

	cfg := GetConfig()
	if cfg == nil {
		panic("load config failed")
	}

	if cfg.App.Route.Port > 0 {
		go updateRouteAccess(cfg.App.Route.Host, cfg.App.Route.Port)
	}

	go func() {
		for {
			time.Sleep(5 * time.Second)
			if route := GetRouteHost(); route != nil {
				updateRouteAccess(route.Host, route.Port)
			}
		}
	}()

	if len(reszipbuffer) > 0 {
		errno.AssertNil(publishZipBuffer(reszipbuffer, "res"))
		httpHandle("/favicon.ico", func(response http.ResponseWriter, request *http.Request) {
			http.Redirect(response, request, "/res/img/logo.png", http.StatusTemporaryRedirect)
		})
	}

	func() {
		initmutex.Lock()
		defer initmutex.Unlock()
		for _, callback := range callbacks {
			if err := callback(); err != nil {
				slog.Error("run init callback error", errno.Attr(err))
				panic(err)
			}
		}
	}()

	errno.AssertNil(http.ListenAndServe(fmt.Sprintf("%s:%d", cfg.App.Host, cfg.App.Port), nil))
}

func Register[RequestType any, ResponseType any](path string, proc func(context.Context, *RequestType, *ResponseType) error) *document {
	path = FormatPath(path)
	doc := addDocument[RequestType, ResponseType](path)
	httpHandle(path, func(writer http.ResponseWriter, request *http.Request) {
		path := strings.ToLower(request.URL.Path)
		if len(path) > 1 {
			path = strings.TrimLeft(path, "/")
		}
		ctx := newContext(path)
		ctx.httpRequest = request
		ctx.httpResponseWriter = writer
		ctx.request = reflect.New(doc.requestType).Interface()
		ctx.responst = reflect.New(doc.responseType).Interface()
		process(ctx, doc, proc)
	})
	return doc
}

func RegisterInit(callback func() error) {
	initmutex.Lock()
	defer initmutex.Unlock()
	callbacks = append(callbacks, callback)
}

func updateRouteAccess(host string, port int) {
	updateRoute(host, port)
	updateAccess(host, port)
}
